// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Admin.NET.Application.Service.PlanViews.Dto;
using Admin.NET.Core;

namespace Admin.NET.Application.Service.PlanViews;

/// <summary>
/// 区域分析服务实现
/// 负责按作业区域进行统计分析
/// </summary>
public class AreaAnalysisService : IAreaAnalysisService, ITransient
{
    private readonly IDataFilterService _dataFilterService;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="dataFilterService">数据筛选服务</param>
    public AreaAnalysisService(IDataFilterService dataFilterService)
    {
        _dataFilterService = dataFilterService;
    }

    /// <summary>
    /// 获取区域分析数据
    /// 按作业区域分组，提供每个区域的完整统计和图表数据
    /// </summary>
    /// <param name="filter">筛选条件</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>区域分析数据列表</returns>
    public async Task<List<AreaItem>> GetAreaDataAsync(BusinessMonthFilter filter, CancellationToken cancellationToken = default)
    {
        try
        {
            // 获取筛选后的数据
            var planData = await _dataFilterService.GetFilteredDataAsync(filter, cancellationToken);
            var (startDate, endDate) = _dataFilterService.GetDateRange(filter);

            // 异步计算区域数据
            var areaItems = await CalculateAreaDataAsync(planData, filter, startDate, endDate, cancellationToken);

            return areaItems;
        }
        catch (Exception ex)
        {
            // 记录错误日志
            throw new Exception($"计算区域分析数据失败: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// 计算区域数据
    /// 按区域分组并计算各项统计指标
    /// </summary>
    /// <param name="planData">计划数据</param>
    /// <param name="filter">筛选条件</param>
    /// <param name="startDate">开始日期</param>
    /// <param name="endDate">结束日期</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>区域分析数据列表</returns>
    private async Task<List<AreaItem>> CalculateAreaDataAsync(List<PlanDataCore> planData, BusinessMonthFilter filter, DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
    {
        var result = new List<AreaItem>();

        // 按区域分组，过滤掉WorkAreaId为null的数据
        var areaGroups = planData
            .Where(p => p.WorkAreaId.HasValue)
            .GroupBy(p => p.WorkAreaId.Value);

        // 并行计算每个区域的数据
        var areaTasks = areaGroups.Select(async group =>
        {
            var areaId = group.Key;
            var areaData = group.ToList();

            // 为每个区域生成简化的图表数据
            var chartData = await Task.Run(() => CalculateAreaChartData(areaData, startDate, endDate), cancellationToken);

            // 计算基础统计
            var totalPlans = areaData.Count;
            var completedPlans = areaData.Count(p => p.ActualEnd.HasValue);
            var completionRate = totalPlans > 0 ? Math.Round((decimal)completedPlans / totalPlans * 100, 2) : 0;

            return new AreaItem
            {
                AreaId = areaId,
                AreaName = GetAreaName(areaId),
                ChartData = chartData,
                TotalPlans = totalPlans,
                CompletedPlans = completedPlans,
                CompletionRate = completionRate
            };
        });

        result = (await Task.WhenAll(areaTasks)).ToList();

        // 按区域ID排序
        return result.OrderBy(x => x.AreaId).ToList();
    }

    /// <summary>
    /// 计算区域的图表数据
    /// 复用月度图表的计算逻辑，但只针对特定区域的数据
    /// </summary>
    /// <param name="areaData">区域计划数据</param>
    /// <param name="startDate">开始日期</param>
    /// <param name="endDate">结束日期</param>
    /// <returns>日度数据列表</returns>
    private List<DailyPoint> CalculateAreaChartData(List<PlanDataCore> areaData, DateTime startDate, DateTime endDate)
    {
        var dailyPoints = new List<DailyPoint>();

        // 预处理数据：按日期分组
        var planStartByDate = areaData
            .Where(p => p.PlanStart.HasValue)
            .GroupBy(p => p.PlanStart.Value.Date)
            .ToDictionary(g => g.Key, g => g.Count());

        var planEndByDate = areaData
            .Where(p => p.PlanEnd.HasValue)
            .GroupBy(p => p.PlanEnd.Value.Date)
            .ToDictionary(g => g.Key, g => g.Count());

        // 重新设计实际开工/完工的统计逻辑：
        // 按实际开工/完工日期分组，但只包含计划开工/完工日期在业务月内的项目
        var actualStartByDate = areaData
            .Where(p => p.ActualStart.HasValue && 
                       p.PlanStart.HasValue &&
                       p.PlanStart.Value.Date >= startDate.Date &&
                       p.PlanStart.Value.Date <= endDate.Date)
            .GroupBy(p => p.ActualStart.Value.Date)
            .ToDictionary(g => g.Key, g => g.Count());

        var actualEndByDate = areaData
            .Where(p => p.ActualEnd.HasValue && 
                       p.PlanEnd.HasValue &&
                       p.PlanEnd.Value.Date >= startDate.Date &&
                       p.PlanEnd.Value.Date <= endDate.Date)
            .GroupBy(p => p.ActualEnd.Value.Date)
            .ToDictionary(g => g.Key, g => g.Count());

        // 计算业务月开始日期之前的实际开工/完工数量（这些要计入第一天）
        var earlyActualStartCount = actualStartByDate
            .Where(kv => kv.Key < startDate.Date)
            .Sum(kv => kv.Value);
        
        var earlyActualEndCount = actualEndByDate
            .Where(kv => kv.Key < startDate.Date)
            .Sum(kv => kv.Value);

        // 累计计数器
        int cumulativePlanStart = 0;
        int cumulativePlanEnd = 0;
        int cumulativeActualStart = earlyActualStartCount; // 从提前开工数量开始
        int cumulativeActualEnd = earlyActualEndCount;     // 从提前完工数量开始

        // 遍历业务月的每一天
        for (var date = startDate.Date; date <= endDate.Date; date = date.AddDays(1))
        {
            // 累计数量
            cumulativePlanStart += planStartByDate.GetValueOrDefault(date, 0);
            cumulativePlanEnd += planEndByDate.GetValueOrDefault(date, 0);
            // 只统计实际开工/完工日期在当天且在业务月内的项目
            var dailyActualStart = actualStartByDate.ContainsKey(date) && date >= startDate.Date && date <= endDate.Date 
                ? actualStartByDate[date] : 0;
            var dailyActualEnd = actualEndByDate.ContainsKey(date) && date >= startDate.Date && date <= endDate.Date
                ? actualEndByDate[date] : 0;
            cumulativeActualStart += dailyActualStart;
            cumulativeActualEnd += dailyActualEnd;

            // 创建日度数据点
            dailyPoints.Add(new DailyPoint
            {
                Date = date.ToString("yyyy-MM-dd"),
                PlanStart = cumulativePlanStart,
                PlanEnd = cumulativePlanEnd,
                ActualStart = cumulativeActualStart,
                ActualEnd = cumulativeActualEnd
            });
        }

        return dailyPoints;
    }

    /// <summary>
    /// 获取区域名称
    /// 这里可以从缓存或数据库字典表获取区域名称
    /// 暂时返回默认格式，后续可以优化
    /// </summary>
    /// <param name="areaId">区域ID</param>
    /// <returns>区域名称</returns>
    private string GetAreaName(long areaId)
    {
        // TODO: 这里应该从区域字典表或缓存中获取真实的区域名称
        // 暂时返回默认格式，后续集成时可以注入相应的服务来获取真实名称
        return $"区域_{areaId}";
    }
}
